
package com.eva.android;

import android.util.Log;

import java.util.ArrayList;
import java.util.Observer;

/**
 * 一个数据变动可通知观察者对象的ArrayList包装实现类.
 *
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @since RainbowChat1.0
 * @param <T> 数据结构中存放的对象，可以是任意{@link java.lang.Object}的子类
 */
public class ArrayListObservable<T> {

	private final static String TAG = ArrayListObservable.class.getSimpleName();

	/** 数组本身 */
	private final ArrayList<T> dataList = new ArrayList<>();
	/** 数据变动观察者列表 */
//  private final ArrayList<Observer> observers = new ArrayList<Observer>();
	private final Observers observers = new Observers();

	public ArrayListObservable() {
		super();
	}
	
	public void set(int index, T cme) {
		this.set(index, cme, true);
	}

	/**
	 * 
	 * @param index
	 * @param cme
	 * @param notifyObserver true表示要通知观察者，此观察者通常用于刷新UI之用，所以可以将此参数理解为更新完数据模型后是否要刷新ui
	 */
	public void set(int index, T cme, boolean notifyObserver) {
		dataList.set(index, cme);
		if(notifyObserver) {
			// 通知观察者
            notifyObservers(new UpdateDataToObserver<T>(UpdateTypeToObserver.set, cme));
		}
	}

	/**
	 * 在集合末尾加入一个元素。
	 * 
	 * @param cme
	 */
	public void add(T cme) {
		this.add(cme, true);
	}

	/**
	 * 在集合末尾加入一个元素。
	 * 
	 * @param cme
	 * @param notifyObserver true表示要通知观察者，此观察者通常用于刷新UI之用，所以可以将此参数理解为更新完数据模型后是否要刷新ui
	 */
	public void add(T cme, boolean notifyObserver) {
		dataList.add(cme);
		if(notifyObserver) {
			// 通知观察者
            notifyObservers(new UpdateDataToObserver<T>(UpdateTypeToObserver.add, cme));
		}
	}
	
	/**
	 * 在指定索引处加入一个元素。
	 * 
	 * @param index
	 * @param cme
	 */
	public void add(int index, T cme) {
		this.add(index, cme, true);
	}

	/**
	 * 在指定索引处加入一个元素。
	 * 
	 * @param index
	 * @param cme
	 * @param notifyObserver true表示要通知观察者，此观察者通常用于刷新UI之用，所以可以将此参数理解为更新完数据模型后是否要刷新ui
	 */
	public void add(int index, T cme, boolean notifyObserver) {
		dataList.add(index, cme);
		if(notifyObserver) {
			// 通知观察者
            notifyObservers(new UpdateDataToObserver<T>(UpdateTypeToObserver.add, cme));
		}
	}
	
	/**
	 * 删除指定索引处的对象
	 *
	 * @param index 被删除对象的数据索引值
	 * @param notifyObserver true表示要通知观察者，此观察者通常用于刷新UI之用，所以可以将此参数理解为更新完数据模型后是否要刷新ui
	 * @return 删除成功则返回被删除的对象，否则返回null
	 */
	public T remove(int index, boolean notifyObserver) {

		if(index < 0){
			Log.w(TAG, "无效的index="+index);
			return null;
		}

		try {
			// 被删除的对象
			T theRemovedElement = dataList.remove(index);
			if (notifyObserver) {
				// 通知观察者
				notifyObservers(new UpdateDataToObserver<T>(UpdateTypeToObserver.remove, theRemovedElement));
			}
			return theRemovedElement;
		}
		catch (Exception e){
			Log.w(TAG, e);
			return null;
		}
	}

	public T get(int index) {
		return dataList.get(index);
	}
	
	public int indexOf(T o) {
		return dataList.indexOf(o);
	}

	public ArrayList<T> getDataList() {
		return dataList;
	}
	
	/**
	 * 用新的集合来覆盖原dataList.
	 * 
	 * <p>注：本方法不是用新的ArrayList<T>对象来替换原dataList
	 * 对象，而是将新集后的所有元素放到被clear后的原dataList集合里，
	 * 也即是说：调用完本方法后，dataList还是原来的对象，只是集合元素改变了而
	 * 已，此举对将dataList引用作为ListView列表数据集的场景中有好处：浅拷贝使得数据随时
	 * 是被同步的（映射到ListView列表中）。
	 * 
	 * <p>注意：此方法将会多次通知观察者.
	 * 
	 * @param newDatas
	 * @see #add(Object)
	 */
	public void putDataList(ArrayList<T> newDatas, boolean notifyObserver) {
		dataList.clear();
		for(T t : newDatas) {
			add(t, notifyObserver);
		}
	}

    public void addObserver(Observer obs) {
        this.observers.add(obs);
    }

    public void removeObserver(Observer obs) {
        this.observers.remove(obs);
    }

    public void removeAllObservers() {
        this.observers.clear();;
    }

    public void notifyObservers(UpdateDataToObserver<T> udto) {
//        for (Observer obs : this.observers) {
//            if(obs != null)
//                obs.update(null, udto);
//        }
		this.observers.notifyAll(udto);
    }
	
	public void clear(boolean notifyObserver) {
		this.dataList.clear();
		if(notifyObserver) {
			// 通知观察者
			notifyObservers(new UpdateDataToObserver<T>(UpdateTypeToObserver.clear, null));
		}
	}
	
	//----------------------------------------------------------------------------- inner class
	/**
	 * 数据变动时将作为变动详细信息提供对象传对观察者.
	 *
	 * @param <T>
	 */
	public static class UpdateDataToObserver<T> {

		private UpdateTypeToObserver updateType = null;
		private T extraData = null;
		
		public UpdateDataToObserver(UpdateTypeToObserver updateType, T extraData) {
			this.updateType = updateType;
			this.extraData = extraData;
		}
		
		public UpdateTypeToObserver getUpdateType() {
			return updateType;
		}

		public void setUpdateType(UpdateTypeToObserver updateType) {
			this.updateType = updateType;
		}

		public T getExtraData() {
			return extraData;
		}

		public void setExtraData(T extraData) {
			this.extraData = extraData;
		}
	}
	
	/**
	 * 数据变动类型。
	 */
	public enum UpdateTypeToObserver {
		/** 清空 */
		clear,
		/** 新加入了行 */
		add,
		/** 移除了行 */
		remove,
		/** 替换了行 */
		set,
        /** 未定义 */
        unknow
	}
}